vmware自定义规范定制虚拟机-python
之前介绍过了vcenter自定义规范定制虚拟机-vsphere client,在图形化界面实现了虚拟机定制,虽然在一定程度上简化了操作,但是我觉得最终的应用场景应该是接入我们内部的自动化平台,打通cmdb、跳板机、监控等组件,因此我们来继续介绍下python模块pyvmomi实现虚拟机定制,希望能给大家带来一定的启发。
环境准备
名称 | 版本 | 备注 |
vCenter | 5.5.0-218311 | vCenter Server5.5 Update 2b |
Centos | 7.5 | 模板自带IP:192.168.3.253 |
pyvmomi | 6.7.3 | |
python | 2.7.15 |
pyvmomi模块提供了一些实例,我们以clone_vm.py例子进行了整合修改。
虚拟机订制流程
pyvmomi实现自定义规范定制和vsphere client不同,它的流程如下:
1.通过clone_vm函数从模板克隆虚拟机,注意克隆完成后不要启动;
2.克隆完成后,在关机状态下通过ip_assign函数按照自定义规范进行定制;
3.自定义完成后,虚拟机是关闭的,需要通过powerOn函数启动;
实现
1.网络配置
def ip_assign(vm, vm_ip, vm_name):
"""设置IP地址"""
adaptermap = vim.vm.customization.AdapterMapping()
adaptermap.adapter = vim.vm.customization.IPSettings()
adaptermap.adapter.ip = vim.vm.customization.FixedIp()
adaptermap.adapter.ip.ipAddress = vm_ip
adaptermap.adapter.subnetMask = "255.255.255.0"
adaptermap.adapter.gateway = "192.168.3.1"
#adaptermap.adapter.dnsDomain = "localhost"
"""dns设置"""
globalip = vim.vm.customization.GlobalIPSettings()
globalip.dnsServerList = "114.114.114.114"
"""设置主机名"""
ident = vim.vm.customization.LinuxPrep()
#ident.domain = "localhost"
ident.hostName = vim.vm.customization.FixedName()
ident.hostName.name = vm_name
customspec = vim.vm.customization.Specification()
customspec.nicSettingMap = [adaptermap]
customspec.globalIPSettings = globalip
customspec.identity = ident
print "Reconfiguring VM Networks . . ."
task = vm.Customize(spec=customspec)
wait_for_task(task)
以上主要实现IP地址设置、dns设置、主机名设置,由于我们在模板中对CPU、MEMORY、DISK等已经定义完毕,基于模板进行定制,因此没有此方面的定义。
2.虚拟机启动
默认自定义完成后,虚拟机是关机状态的,我们需要开机操作才能和其他流程整合。
#根据虚拟机名获取obj
vm = get_obj(content, [vim.VirtualMachine], args.vm_name)
#执行开机操作
task = vm.PowerOn()
wait_for_task(task)
3.clone_vm_customize.py实现
clone_vm.py 是只对虚拟机模板的克隆,不具有自定义规范的功能,因此我们通过对其进行网络配置、虚拟机启动进行整合,才能实现真正的自定义克隆模板的定制。
基于clone_vm.py整合如下:
vim clone_vm_customize.py
#!/usr/bin/env python
#-*- coding: utf-8 -*-
"""
Written by Dann Bohn
Github: https://github.com/whereismyjetpack
Email: dannbohn@gmail.com
Clone a VM from template example
"""
from pyVmomi import vim
from pyVim.connect import SmartConnect, SmartConnectNoSSL, Disconnect
import atexit
import argparse
import getpass
from add_nic_to_vm import add_nic
def get_args():
""" Get arguments from CLI """
parser = argparse.ArgumentParser(
description='Arguments for talking to vCenter')
parser.add_argument('-s', '--host',
required=True,
action='store',
help='vSpehre service to connect to')
parser.add_argument('-o', '--port',
type=int,
default=443,
action='store',
help='Port to connect on')
parser.add_argument('-u', '--user',
required=True,
action='store',
help='Username to use')
parser.add_argument('-p', '--password',
required=False,
action='store',
help='Password to use')
parser.add_argument('-v', '--vm-name',
required=True,
action='store',
help='Name of the VM you wish to make')
parser.add_argument('--no-ssl',
action='store_true',
help='Skip SSL verification')
parser.add_argument('--template',
required=True,
action='store',
help='Name of the template/VM \
you are cloning from')
parser.add_argument('--datacenter-name',
required=False,
action='store',
default=None,
help='Name of the Datacenter you\
wish to use. If omitted, the first\
datacenter will be used.')
parser.add_argument('--vm-folder',
required=False,
action='store',
default=None,
help='Name of the VMFolder you wish\
the VM to be dumped in. If left blank\
The datacenter VM folder will be used')
parser.add_argument('--datastore-name',
required=False,
action='store',
default=None,
help='Datastore you wish the VM to end up on\
If left blank, VM will be put on the same \
datastore as the template')
parser.add_argument('--datastorecluster-name',
required=False,
action='store',
default=None,
help='Datastorecluster (DRS Storagepod) you wish the VM to end up on \
Will override the datastore-name parameter.')
parser.add_argument('--cluster-name',
required=False,
action='store',
default=None,
help='Name of the cluster you wish the VM to\
end up on. If left blank the first cluster found\
will be used')
parser.add_argument('--resource-pool',
required=False,
action='store',
default=None,
help='Resource Pool to use. If left blank the first\
resource pool found will be used')
parser.add_argument('--power-on',
dest='power_on',
action='store_true',
help='power on the VM after creation')
parser.add_argument('--opaque-network',
required=False,
help='Name of the opaque network to add to the VM')
args = parser.parse_args()
if not args.password:
args.password = getpass.getpass(
prompt='Enter password')
return args
def wait_for_task(task):
""" wait for a vCenter task to finish """
task_done = False
while not task_done:
if task.info.state == 'success':
return task.info.result
if task.info.state == 'error':
print("there was an error")
task_done = True
def get_obj(content, vimtype, name):
"""
Return an object by name, if name is None the
first found object is returned
"""
obj = None
container = content.viewManager.CreateContainerView(
content.rootFolder, vimtype, True)
for c in container.view:
if name:
if c.name == name:
obj = c
break
else:
obj = c
break
return obj
def ip_assign(vm, vm_ip, vm_name):
"""自定义规范设置"""
"""设置IP地址"""
adaptermap = vim.vm.customization.AdapterMapping()
adaptermap.adapter = vim.vm.customization.IPSettings()
adaptermap.adapter.ip = vim.vm.customization.FixedIp()
adaptermap.adapter.ip.ipAddress = vm_ip
adaptermap.adapter.subnetMask = "255.255.255.0"
adaptermap.adapter.gateway = "192.168.3.1"
#adaptermap.adapter.dnsDomain = "localhost"
"""dns设置"""
globalip = vim.vm.customization.GlobalIPSettings()
globalip.dnsServerList = "114.114.114.114"
"""设置主机名"""
ident = vim.vm.customization.LinuxPrep()
#ident.domain = "localhost"
ident.hostName = vim.vm.customization.FixedName()
ident.hostName.name = vm_name
customspec = vim.vm.customization.Specification()
customspec.nicSettingMap = [adaptermap]
customspec.globalIPSettings = globalip
customspec.identity = ident
print "Reconfiguring VM Networks . . ."
#task = get_obj([vim.VirtualMachine],vm).Customize(spec=customspec)
task = vm.Customize(spec=customspec)
wait_for_task(task)
def clone_vm(
content, template, vm_name, si,
datacenter_name, vm_folder, datastore_name,
cluster_name, resource_pool, power_on, datastorecluster_name):
"""
Clone a VM from a template/VM, datacenter_name, vm_folder, datastore_name
cluster_name, resource_pool, and power_on are all optional.
"""
# if none git the first one
datacenter = get_obj(content, [vim.Datacenter], datacenter_name)
if vm_folder:
destfolder = get_obj(content, [vim.Folder], vm_folder)
else:
destfolder = datacenter.vmFolder
if datastore_name:
datastore = get_obj(content, [vim.Datastore], datastore_name)
else:
datastore = get_obj(
content, [vim.Datastore], template.datastore[0].info.name)
# if None, get the first one
cluster = get_obj(content, [vim.ClusterComputeResource], cluster_name)
if resource_pool:
resource_pool = get_obj(content, [vim.ResourcePool], resource_pool)
else:
resource_pool = cluster.resourcePool
vmconf = vim.vm.ConfigSpec()
if datastorecluster_name:
podsel = vim.storageDrs.PodSelectionSpec()
pod = get_obj(content, [vim.StoragePod], datastorecluster_name)
podsel.storagePod = pod
storagespec = vim.storageDrs.StoragePlacementSpec()
storagespec.podSelectionSpec = podsel
storagespec.type = 'create'
storagespec.folder = destfolder
storagespec.resourcePool = resource_pool
storagespec.configSpec = vmconf
try:
rec = content.storageResourceManager.RecommendDatastores(
storageSpec=storagespec)
rec_action = rec.recommendations[0].action[0]
real_datastore_name = rec_action.destination.name
except:
real_datastore_name = template.datastore[0].info.name
datastore = get_obj(content, [vim.Datastore], real_datastore_name)
# set relospec
relospec = vim.vm.RelocateSpec()
relospec.datastore = datastore
relospec.pool = resource_pool
clonespec = vim.vm.CloneSpec()
clonespec.location = relospec
clonespec.powerOn = power_on
print("cloning VM...")
task = template.Clone(folder=destfolder, name=vm_name, spec=clonespec)
wait_for_task(task)
def main():
"""
Let this thing fly
"""
args = get_args()
# connect this thing
si = None
if args.no_ssl:
si = SmartConnectNoSSL(
host=args.host,
user=args.user,
pwd=args.password,
port=args.port)
else:
si = SmartConnect(
host=args.host,
user=args.user,
pwd=args.password,
port=args.port)
# disconnect this thing
atexit.register(Disconnect, si)
content = si.RetrieveContent()
template = None
template = get_obj(content, [vim.VirtualMachine], args.template)
if template:
#克隆模板
clone_vm(
content, template, args.vm_name, si,
args.datacenter_name, args.vm_folder,
args.datastore_name, args.cluster_name,
args.resource_pool, args.power_on, args.datastorecluster_name)
vm = get_obj(content, [vim.VirtualMachine], args.vm_name)
if args.opaque_network:
add_nic(si, vm, args.opaque_network)
#自定义规范定制虚拟机
ip_assign(vm, "192.168.3.254", args.vm_name)
else:
print("template not found")
#启动虚拟机
vm = get_obj(content, [vim.VirtualMachine], args.vm_name)
task = vm.PowerOn()
wait_for_task(task)
# start this thing
if __name__ == "__main__":
main()
```
4.执行命令
#-s: vcenter地址
#-u: vcenter账户
#-p: vcneter密码
#-v: 虚拟机和主机名we123
#--template: 克隆的模板名称template_centos7
#--datacenter-name: 数据中心unicom-idc
#--vm-folder:新建虚拟机所在的文件夹test
#--datastore-name: 虚拟机挂载的存储vm.datastore1
#--cluster-name: 虚拟机所在集群idc
python clone_vm_customize.py -s 192.168.3.xxx -u vcenter@vsphere.local -p xxxxxxx -v we123 --template template_centos7 --datacenter-name unicom-idc --vm-folder test --datastore-name vm.datastore1 --cluster-name idc --no-ssl
执行完成后,根据ip_assign(vm, "192.168.3.254", args.vm_name)实现虚拟机的网络定制如下:
ip地址:192.168.3.254;
子网掩码:255.255.255.0;
网关:192.168.3.1;
DNS:114.114.114.114;
虚拟机名和主机名:we123;
总结
经过实验对比,此次的虚拟机定制过程可以在不到3分钟的时间内交付一台虚拟机,大大的提高了运维的工作效率。至于交付的虚拟机剩余工作,我们需要结合自己的实际情况与其他组件进行对接。